package br.com.pamc.scan;

import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.List;

import org.apache.commons.cli.CommandLine;
import org.apache.commons.cli.CommandLineParser;
import org.apache.commons.cli.GnuParser;
import org.apache.commons.cli.HelpFormatter;
import org.apache.commons.cli.Option;
import org.apache.commons.cli.OptionBuilder;
import org.apache.commons.cli.Options;
import org.apache.commons.cli.ParseException;

public class Scan {

	/**
	 * Scans the directory behind your files.
	 * 
	 * @param path
	 * @return
	 */
	public static List<File> scanDirectory(File path, final List<File> results) {

		if (path.isDirectory()) {

			File[] children = path.listFiles();

			for (int i = 0; i < children.length; i++) {
				
				results.add(children[i]);

				scanDirectory(children[i], results);

			}

		}

		return results;

	}

	/**
	 * Saves the result of scanning the file output.
	 * 
	 * @param outputFile
	 * @param files
	 */
	public static void writeToOutputFile(File outputFile, List<File> files) {

		try {

			BufferedWriter writer = new BufferedWriter(new FileWriter(outputFile));

			for (File file : files) {

				writer.write(file.getAbsolutePath());
				writer.newLine();
				writer.flush();

			}

			writer.close();

		} catch (IOException e) {

			System.err.println(e.getMessage());
			System.exit(1);

		}

	}

	/**
	 * Sets options that the program supports.
	 * 
	 * @return options
	 */
	private static Options setsOptions() {

		final Options options = new Options();

		Option optionDisplayHelp = OptionBuilder.withLongOpt("help")
				.hasArg(false).isRequired(false)
				.withDescription("Option to display help.").create("h");
		options.addOption(optionDisplayHelp);

		Option optionDirectoryScanned = OptionBuilder.withLongOpt("path")
				.hasArg(true).isRequired(false)
				.withDescription("Option to set the directory to scan.")
				.create("p");
		options.addOption(optionDirectoryScanned);

		Option optionResultFile = OptionBuilder.withLongOpt("file")
				.hasArg(true).isRequired()
				.withDescription("Option to set the result file.").create("f");
		options.addOption(optionResultFile);

		return options;
	}

	/**
	 * Prints help.
	 * 
	 * @param options
	 */
	private static void printsHelp(final Options options) {

		final String cmdLineSyntax = "java -jar scan.jar";
		final HelpFormatter helpFormatter = new HelpFormatter();

		helpFormatter.printHelp(cmdLineSyntax, options);
	}

	/**
	 * Performs parsing of options.
	 * 
	 * @param options
	 * @param args
	 */
	private static void parsesOptions(final Options options, final String[] args) {

		final CommandLineParser cmdLineGnuParser = new GnuParser();

		CommandLine commandLine;

		try {

			commandLine = cmdLineGnuParser.parse(options, args);

			if (commandLine.hasOption("h")) {
				printsHelp(options);
				System.exit(1);
			}

			File path = null;

			if (commandLine.hasOption("p")) {

				String argumentPath = commandLine.getOptionValue("p");

				path = new File(argumentPath);

				if (!path.exists() || !path.isDirectory()) {
					throw new ParseException(MessageFormat.format(
							"The argument {0} is not a directory.",
							argumentPath));
				}

			} else {
				path = new File(".");
			}

			File file = null;

			if (commandLine.hasOption("f")) {

				String argumentFile = commandLine.getOptionValue("f");

				file = new File(argumentFile);

				if (file.exists() && !file.canWrite()) {
					throw new ParseException(MessageFormat.format(
							"{0} file is not writable.", argumentFile));
				}

			}

			writeToOutputFile(file, scanDirectory(path, new ArrayList<File>()));

		} catch (ParseException parseException) {
			System.err.println(parseException.getMessage());
		}

	}

	public static void main(String[] args) {

		// Sets options that the program supports.
		final Options options = setsOptions();
		
		if (args.length < 1) {
			
			// If there are no arguments, the help will be displayed.
			printsHelp(options);
			
		} else {
			
			// Analyzes options passed.
			parsesOptions(options, args);
			
		}

	}

}
